﻿Imports DERRA.InteliNPC
Imports DERRA.Tasking
Imports GTA
Imports GTA.Math
Imports GTA.UI

Public Module MissionHelper
    Private m_enermyRelationship As RelationshipGroup?
    Private random As Random = New Random()
    Public Function PickEnum(Of T)(ParamArray except As T()) As T
        Dim values As Array = [Enum].GetValues(GetType(T))
        Dim list As List(Of T) = New List(Of T)(values.Length)
        For Each value As T In values
            If Not except.Contains(value) Then
                list.Add(value)
            End If
        Next
        Return Pick(list)
    End Function
    Public Function Pick(Of T)(list As IList(Of T)) As T
        Return list.Item(random.Next(list.Count))
    End Function
    Public Function Pick(max_value As Integer) As Integer
        Return random.Next(max_value + 1)
    End Function
    Public Function Pick(max_value As Integer, min_value As Integer) As Integer
        Return min_value + System.Math.Round((max_value - min_value) * random.NextDouble())
    End Function
    Public ReadOnly Property EnermyRelationshipGroup As RelationshipGroup
        Get
            If Not m_enermyRelationship.HasValue Then
                m_enermyRelationship = World.AddRelationshipGroup("derras_enermy")
                PlayerPed.RelationshipGroup.SetRelationshipBetweenGroups(m_enermyRelationship, Relationship.Hate)
            End If
            Return m_enermyRelationship
        End Get
    End Property
    Public Function CanPlayerSee(entity As Entity) As Boolean
        Return entity.IsOnScreen AndAlso entity.Position.DistanceTo(PlayerPed.Position) < 200
    End Function
    Private Function GetFreePassengerSeat(vehicle As Vehicle) As Integer?
        If vehicle Is Nothing OrElse vehicle.IsDead Then
            Return Nothing
        End If
        Dim count As Integer = 0
        For Each seat As VehicleSeat In [Enum].GetValues(GetType(VehicleSeat))
            If seat <> VehicleSeat.Driver AndAlso seat <> VehicleSeat.None Then
                count += 1
            End If
        Next
        Return count
    End Function

    ''' <summary>
    ''' 生成梅里韦瑟打手
    ''' </summary>
    Public Sub DispatchGoons()
        Dim vehicle1 As Vehicle = CreateVehicle(VehicleHash.Baller6, 200)
        For Each seat As VehicleSeat In {VehicleSeat.Driver, VehicleSeat.RightFront, VehicleSeat.LeftRear, VehicleSeat.RightRear}
            Dim cop As Ped = vehicle1.CreatePedOnSeat(seat, PedHash.Blackops03SMY)
            cop.RelationshipGroup = EnermyRelationshipGroup
            cop.CombatAbility = CombatAbility.Professional
            cop.CombatMovement = CombatMovement.WillAdvance
            cop.CombatRange = CombatRange.Near
            cop.Weapons.Give(WeaponHash.MiniSMG, 1000, True, True)
            cop.Weapons.Give(WeaponHash.SpecialCarbine, 1000, True, True)
            Dim controller As EnermyController = New EnermyController(cop, "打手")
            EntityManagement.AddController(controller)
            cop.Task.Combat(PlayerPed)
        Next
        vehicle1.MarkAsNoLongerNeeded()
        Dim vehicle2 As Vehicle = World.CreateVehicle(VehicleHash.Baller6, World.GetNextPositionOnStreet(vehicle1.FrontPosition, True))
        vehicle2.PlaceOnNextStreet()
        For Each seat As VehicleSeat In {VehicleSeat.Driver, VehicleSeat.RightFront, VehicleSeat.LeftRear, VehicleSeat.RightRear}
            Dim cop As Ped = vehicle2.CreatePedOnSeat(seat, PedHash.Blackops03SMY)
            cop.Style.RandomizeOutfit()
            cop.Style.RandomizeProps()
            cop.RelationshipGroup = EnermyRelationshipGroup
            cop.CombatAbility = CombatAbility.Average
            cop.CombatMovement = CombatMovement.WillAdvance
            cop.CombatRange = CombatRange.Near
            cop.Weapons.Give(WeaponHash.SpecialCarbine, 1000, True, True)
            cop.Armor = 200
            Dim controller As EnermyController = New EnermyController(cop, "打手")
            EntityManagement.AddController(controller)
            cop.Task.Combat(PlayerPed)
        Next
        vehicle2.MarkAsNoLongerNeeded()
    End Sub
    Public Class BodyguardHelper
        Private ReadOnly ped As Ped
        Private ReadOnly ownedVehicles As VehicleCollection
        Private positionChanged As Boolean
        Private lastPosition As Vector3?
        Private positionNotChanged As Integer
        Private Const GroupDistance As Single = 30
        Public Sub New(ped As Ped, ownedVehicles As VehicleCollection)
            Me.ped = ped
            Me.ownedVehicles = ownedVehicles
        End Sub
        Private Sub Log(message As String)
            'Notification.PostTicker(ped.Handle & " " + message, True, False)
        End Sub
        Public Sub UpdateStandardBodyguardTask()
            If lastPosition.HasValue Then
                If ped.Position = lastPosition Then
                    positionChanged = False
                    positionNotChanged += 1
                Else
                    positionChanged = True
                    positionNotChanged = 0
                End If
            End If
            lastPosition = ped.Position
            If positionNotChanged > 3 Then
                If Not ped.IsAiming AndAlso Not ped.IsShooting AndAlso Not ped.IsInCover AndAlso Not ped.IsInVehicle AndAlso ped.Position.DistanceTo(PlayerPed.Position) < GroupDistance Then
                    '卡住了
                    If ped.IsInCombat Then
                        Dim enermy As Ped = ped.CombatTarget
                        If enermy IsNot Nothing Then
                            If enermy.Position.DistanceTo(ped.Position) > 50 Then
                                Log("由于敌人太远卡住了")
                                ped.Task.ClearAll()
                                ped.Task.RunTo(PlayerPed.Position, 5000)
                            Else
                                Log("打个人都能卡住去死吧")
                                ped.Kill()
                            End If
                        Else
                            Log("NativeFunction返回结果冲突，去死吧")
                            ped.Kill()
                        End If
                    Else
                        Log("又卡住了")
                        If positionNotChanged > 5 Then
                            Log("怎么修都修不好，去死吧")
                            ped.Kill()
                        Else
                            ped.Task.RunTo(PlayerPed.Position, 5000)
                        End If
                    End If


                End If
            End If
            Dim distance As Single = ped.Position.DistanceTo(PlayerPed.Position)
            If distance < 70 Then
                If Not ped.IsInCombat Then '如果ped处于空闲状态
                    Dim enermy As Ped = ThreatDetector.GetNearlistTreat(ped.Position, 50, AddressOf ThreatDetector.IsCommonThreat)
                    If enermy IsNot Nothing Then '检测敌人
                        ped.LeaveGroup()
                        ped.Task.ClearAll()
                        ped.Task.Combat(enermy)
                    Else
                        If Not ped.IsInGroup Then
                            If distance < GroupDistance Then
                                If PlayerPed.IsInVehicle() Then
                                    If GetFreePassengerSeat(PlayerPed.CurrentVehicle).GetValueOrDefault(0) > 0 Then
                                        ped.Task.ClearAll()
                                        PlayerPed.PedGroup.Add(ped, False)
                                    End If
                                Else
                                    ped.Task.ClearAll()
                                    PlayerPed.PedGroup.Add(ped, False)
                                End If
                            Else '距离大于30米小于70米
                                If Not positionChanged AndAlso Not ped.IsInVehicle() AndAlso ped.VehicleTryingToEnter IsNot Nothing Then
                                    ped.Task.FollowToOffsetFromEntity(PlayerPed, Vector3.Zero, 2)
                                    'UI.Screen.ShowSubtitle("保镖正在步行前往玩家位置")
                                ElseIf ped.IsInVehicle() Then
                                    ped.Task.VehicleFollow(ped.CurrentVehicle, PlayerPed, 70, VehicleDrivingFlags.DrivingModePloughThrough)
                                End If
                            End If
                        End If
                    End If
                Else 'ped正在作战
                    Dim target As Ped = ped.CombatTarget
                    If target IsNot Nothing Then
                        If target.Position.DistanceTo(ped.Position) < 50 Then
                            If ped.IsInGroup AndAlso Not IsInSameVehicle(PlayerPed, ped) Then
                                ped.LeaveGroup()
                            End If
                        Else
                            ped.Task.ClearAll()
                        End If
                    End If

                End If
            Else '距离玩家太远
                If Not PlayerPed.IsInVehicle() Then '玩家不在载具中
                    Dim v As Vehicle = ped.CurrentVehicle
                    If v IsNot Nothing Then
                        If distance > 200 Then
                            v.Position = World.GetNextPositionOnStreet(PlayerPed.Position.Around(150), True)
                            v.PlaceOnNextStreet()
                        End If
                    Else
                        v = CreateVehicle(ownedVehicles.GetRandomVehicleModel(False), System.Math.Min(150, distance))
                        ped.SetIntoVehicle(v, VehicleSeat.Driver)
                        v.MarkAsNoLongerNeeded()
                    End If
                    ped.Task.VehicleFollow(ped.CurrentVehicle, PlayerPed, 70, VehicleDrivingFlags.DrivingModePloughThrough)
                Else '玩家在载具中
                    Dim v As Vehicle = ped.CurrentVehicle
                    If v IsNot Nothing Then '保镖在载具中 
                        ped.Task.VehicleFollow(ped.CurrentVehicle, PlayerPed, 70, VehicleDrivingFlags.DrivingModePloughThrough)
                    Else '
                        v = CreateVehicle(ownedVehicles.GetRandomVehicleModel(False), 10, ped)
                        ped.SetIntoVehicle(v, VehicleSeat.Driver)
                        v.MarkAsNoLongerNeeded()
                    End If
                End If
            End If
        End Sub
    End Class
    ''' <summary>
    ''' 刷出来的敌人控制器，拥有不会消失。
    ''' </summary>
    Public Class EnermyController
        Implements IEntityController
        Private ReadOnly enermy As Ped
        Private ReadOnly blipName As String

        Public Sub New(enermy As Ped, blipName As String)
            Me.enermy = enermy
            enermy.IsEnemy = True
            Me.blipName = blipName
        End Sub
        Public ReadOnly Property Target As Entity Implements IEntityController.Target
            Get
                Return enermy
            End Get
        End Property

        Public Sub OnTick() Implements IEntityController.OnTick
            If enermy.AttachedBlip Is Nothing Then
                With enermy.AddBlip()
                    .IsFriendly = False
                    .Scale = 0.7
                    .Name = blipName
                End With
            End If
            If Not enermy.IsInCombat Then
                enermy.Task.Combat(PlayerPed, taskThreatResponseFlags:=TaskThreatResponseFlags.CanFightArmedPedsWhenNotArmed)
            End If
        End Sub

        Public Sub Disposing() Implements IEntityController.Disposing
        End Sub

        Public Function CanDispose() As Boolean Implements IEntityController.CanDispose
            If enermy.IsDead Then
                Return True
            Else
                Return False
            End If
        End Function
    End Class
End Module
